package 回溯算法.解数独;

public class 解数独_37 {
    public boolean solveSudoku(char[][] board) {
        return backtrack(board, 0, 0);
    }

    public boolean backtrack(char[][] board, int row, int col) {
        //题目规定的棋盘大小
        int m = 9;
        int n = 9;

        if (col == n) {//到达最后一列了，就换到下一行重新开始
            return backtrack(board, row + 1, 0);
        }
        //已经遍历完了最后一行。注意row是从0-8,所以row到8相当于已经到了最后一行，这里row=9，相当于已经遍历完了最后一行的所有列
        if (row == m) {
            //能遍历完最后一行并且放置成功，那肯定是有解的。因为如果没解，是放置不完最后一行的
            return true;
        }

        if (board[row][col] != '.') {
            //该位置预设的有数，不用我们放置
            return backtrack(board, row, col + 1);
        }

        for (char ch = '1'; ch <= '9'; ch++) {//可选择列表：1-9
            //合法性校验
            if (!isValid(board, row, col, ch)) {
                continue;
            }
            //添加到路径
            board[row][col] = ch;
            //回溯
            //如果成功就直接返回，不需要再尝试了。找到一个方案就行，比如可以放1 2，我只要放1成功就立即返回，不要再去尝试2
            if (backtrack(board, row, col + 1)) {
                return true;
            }

            //撤销选择
            board[row][col] = '.';

        }


        ///如果当前位置[row，col]不能放任何数字，那就需要前面的格子换个数字穷举
        return false;
    }

    //验证当前位置[row，col]是否可以存放字符ch
    private static boolean isValid(char[][] board, int row, int col, char ch) {
        for (int i = 0; i < 9; i++) {
            //当前列有没有和字符ch重复的
            if (board[i][col] == ch) {
                return false;
            }
            //当前行有没有和字符ch重复的
            if (board[row][i] == ch) {
                return false;
            }
            //当前的3*3单元格内是否有和字符ch重复的
            if (board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == ch) {
                return false;
            }
        }
        return true;
    }
}
